









// 顶点数组 模型空间顶点坐标
Vector3[] vertices =
{
// Front
// point[1]
new Vector3(-2.0f, 5.0f, -2.0f),//[0]
// point[2]
new Vector3(-2.0f, 0.0f, -2.0f),//[1]
// point[3]
new Vector3(2.0f, 0.0f, -2.0f),//[2]
// point[4]
new Vector3(2.0f, 5.0f, -2.0f),//[3]
// Left
new Vector3(-2.0f, 5.0f, -2.0f),//[4]
new Vector3(-2.0f, 0.0f, -2.0f),//[5]
new Vector3(-2.0f, 0.0f, 2.0f),//[6]
new Vector3(-2.0f, 5.0f, 2.0f),//[7]
// Back
new Vector3(-2.0f, 5.0f, 2.0f),//[8]
new Vector3(-2.0f, 0.0f, 2.0f),//[9]
new Vector3(2.0f, 0.0f, 2.0f),//[10]
new Vector3(2.0f, 5.0f, 2.0f),//[11]
// Right
new Vector3(2.0f, 5.0f, 2.0f),//[12]
new Vector3(2.0f, 0.0f, 2.0f),//[13]
new Vector3(2.0f, 0.0f, -2.0f),//[14]
new Vector3(2.0f, 5.0f, -2.0f),//[15]
// Top
new Vector3(-2.0f, 5.0f, 2.0f),//[16]
new Vector3(2.0f, 5.0f, 2.0f),//[17]
new Vector3(2.0f, 5.0f, -2.0f),//[18]
new Vector3(-2.0f, 5.0f, -2.0f),//[19]
// Bottom
new Vector3(-2.0f, 0.0f, 2.0f),//[20]
new Vector3(2.0f, 0.0f, 2.0f),//[21]
new Vector3(2.0f, 0.0f, -2.0f),//[22]
new Vector3(-2.0f, 0.0f, -2.0f),//[23]
}; // 索引数组
int[] triangles =
{
// Front
2,1,0,
0,3,2,
// Left
4,5,6,
4,6,7,
// Back
9,11,8,
9,10,11,
// Right
12,13,14,
12,14,15,
// Top
16,17,18,
16,18,19,
// Buttom
21,23,22,
21,20,23,
};

// UV数组
Vector2[] uvs =
{
// point[1]
new Vector2(0.0f, 1.0f),
// point[2]
new Vector2(0.0f, 0.0f),
// point[3]
new Vector2(1.0f, 0.0f),
// point[4]
new Vector2(1.0f, 1.0f),
// Left
new Vector2(1.0f, 1.0f),
new Vector2(1.0f, 0.0f),
new Vector2(0.0f, 0.0f),
new Vector2(0.0f, 1.0f),
// Back
new Vector2(1.0f, 1.0f),
new Vector2(1.0f, 0.0f),
new Vector2(0.0f, 0.0f),
new Vector2(0.0f, 1.0f),
// Right
new Vector2(1.0f, 1.0f),
new Vector2(1.0f, 0.0f),
new Vector2(0.0f, 0.0f),
new Vector2(0.0f, 1.0f),
// Top
new Vector2(0.0f, 1.0f),
new Vector2(1.0f, 1.0f),
new Vector2(1.0f, 0.0f),
new Vector2(0.0f, 0.0f),
// Bottom
new Vector2(0.0f, 0.0f),
new Vector2(1.0f, 0.0f),
new Vector2(1.0f, 1.0f),
new Vector2(0.0f, 1.0f),
}; // 新建一个Mesh
Mesh mesh = new Mesh();
// 用构建的数据初始Mesh
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uvs;
// 法线是根据顶点数据计算出来的,所以在修改完顶点后,需要更新一下法线
mesh.RecalculateNormals();
// 将构建好的Mesh替换上
gameObject.GetComponent<MeshFilter>().mesh = mesh;




// Shader 的路径名称 默认为文件名,也可以与文件名不同
Shader "Unlit/HiShader"
{
// 属性
// Material Inspector显示的所有参数都在需要在这里进行声明
Properties
{
// 通常所有属性名都以下划线字符开头 _MainTex
_MainTex ("Texture", 2D) = "white" {}
// 比较常见的属性类型
// ————————————————————————————————————————————————
_Integer ("整数(新版)", Integer) = 1
_Int ("整数(旧版)", Int) = 1
_Float ("浮点数", Float) = 0.5
_FloatRange ("浮点数滑动条", Range(0.0, 1.0)) = 0.5
// Unity包含以下内置纹理, 可以直接填充
// “white”(RGBA:1,1,1,1)
// “black”(RGBA:0,0,0,1)
// “gray”(RGBA:0.5,0.5,0.5,1)
// “bump”(RGBA:0.5,0.5,1,0.5)
// “red”(RGBA:1,0,0,1)
_Texture2D ("2D纹理贴图", 2D) = "red" {}
// 字符串留空或输入无效值,则它默认为 “gray”
_DefaultTexture2D ("2D纹理贴图", 2D) = "" {}
// 默认值为 “gray”(RGBA:0.5,0.5,0.5,1)
_Texture3D ("3D纹理贴图", 3D) = "" {}
_Cubemap ("立方体贴图", Cube) = "" {}
// Inspector会显示四个单独的浮点数字段
_Vector ("Example vector", Vector) = (0.25, 0.5, 0.5, 1)
// Inspector会显示拾色器拾取色彩RGBA值
_Color("色彩", Color) = (0.25, 0.5, 0.5, 1)
// ————————————————————————————————————————————————
// 除此之外 属性声明还可以具有一个可选特性 用来告知Unity如何处理它们
// HDR可以使色彩亮度的值超过1
[HDR]_HDRColor("HDR色彩", Color) = (1,1,1,1)
// Inspector隐藏此属性
[HideInInspector]_Hide("看不见我~", Color) = (1,1,1,1)
// Inspector隐藏此纹理属性的Scale Offset字段
[NoScaleOffset]_HideScaleOffset("隐藏ScaleOffset", 2D) = "" {}
// 指示纹理属性为法线贴图,如果分配了不兼容的纹理,编辑器则会显示警告。
[Normal]_Normal("法线贴图", 2D) = "" {}
}
// 子着色器
// 一个Shader至少有一个或者多个子着色器SubShader,这些子着色器互不干扰,且只有一个会运行
// 在加载shader时Unity会遍历所有SubShader列表,并最终选择用户机器支持的第一个
SubShader
{
// 可以通过Tags来向子着色器分配标签
// 只可以写在SubShader语块内,不可写在Pass内
/* 以键值对的形式存在,可以出现多个键值对
Tags {
"TagName1" = "Value1"
"TagName2" = "Value2"
"TagName3" = "Value3"
...
}
*/
// RenderPipeline: 声明子着色器是否与通用渲染管线 (URP) 或高清渲染管线 (HDRP) 兼容
// 仅与 URP 兼容
// Tags { "RenderPipeline"="UniversalRenderPipeline" }
// 仅与 HDRP 兼容
// Tags { "RenderPipeline"="HighDefinitionRenderPipeline" }
// RenderPipeline不声明或任何其他值表示与 URP 和 HDRP 不兼容
// ————————————————————————————————————————————————
// Queue: 声明渲染队列
// Tags { "Queue"="Background" } // 最早被调用的渲染,用来渲染天空盒或者背景
// Tags { "Queue"="Geometry" } // 这是默认值,用来渲染非透明物体(普通情况下,场景中的绝大多数物体应该是非透明的)
// Tags { "Queue"="AlphaTest" } // 用来渲染经过Alpha Test的像素,单独为AlphaTest设定一个Queue是出于对效率的考虑
// Tags { "Queue"="Transparent" }// 以从后往前的顺序渲染透明物体
// Tags { "Queue"="Overlay" } // 用来渲染叠加的效果,是渲染的最后阶段(比如镜头光晕等特效)
// ————————————————————————————————————————————————
// RenderType: 用来区别这个Shader要渲染的对象是属于什么类别的。
// 设置渲染类型 用一种称为着色器替换的技术在运行时交换子着色器,用来区别这个Shader要渲染的对象是属于什么类别的
// 这里表示非透明物体渲染
Tags { "RenderType"="Opaque" }
// 更多详细内容可参考官网文档 https://docs.unity.cn/cn/2021.3/Manual/SL-SubShaderTags.html
// LOD (Level of Detail)
LOD 100
// 每个子着色器由多个通道组成,许多简单的着色器只使用一个通道,但想要一些更复杂的效果,着色器可能需要更多通道
// 一个Pass就是一次绘制,可以看成是一个Draw Call而Pass的意义在于多次渲染,
// 如果你有一个Pass,那么着色器只会被调用一次,如果你有多个Pass的话,
// 那么就相当于执行多次SubShader了,这就叫双通道或者多通道。
// Draw Call:其实就是CPU调用图像编程接口的渲染命令,CPU每次调用DrawCall,都需要向GPU发送许多数据啊、渲染状态等等,
// 一旦CPU执行完应用阶段,GPU就会开始执行这次的渲染流程。而GPU渲染的速度比CPU提交命令的速度要快的多,
// 所以如果DrawCall数量过多的情况下,CPU需要进行大量的计算,进而就会导致CPU过载,影响游戏的运行效率。
Pass
{
CGPROGRAM
// 声明顶点着色器
#pragma vertex vert
// 声明像素着色器
#pragma fragment frag
// 使雾生效
#pragma multi_compile_fog
// 引用CG的核心代码库
#include "UnityCG.cginc"
// 应用程序阶段结构体
struct appdata
{
// 参考:https://docs.microsoft.com/zh-cn/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics
// POSITION 着色器语言的语义,用来限定着色器的输入输出值的类型
// 模型空间的顶点坐标
float4 vertex : POSITION;
// 模型的第一套UV坐标
float2 uv : TEXCOORD0;
};
struct v2f
{
// UV
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
// SV_POSITION 当这个值需要作为输出值输出给系统用的时候 前面需要加SV_前缀
// 当然因为有向下兼容的机制 不加也没啥太大问题
float4 vertex : SV_POSITION;
};
// 在Properties中声明的参数要在这里相对应的定义后才可以使用
sampler2D _MainTex;
float4 _MainTex_ST;
// 定义顶点着色器函数 函数名要与声明顶点着色器名称相同
v2f vert (appdata v)
{
v2f o;
// 将顶点坐标从模型空间变换到裁剪空间
o.vertex = UnityObjectToClipPos(v.vertex);
// Transforms 2D UV by scale/bias property
// #define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
// 等价于v.uv.xy * _MainTex_ST.xy + _MainTex_ST.zw;
// 简单来说,TRANSFORM_TEX主要作用是拿顶点的uv去和材质球的tiling和offset作运算,
// 确保材质球里的缩放和偏移设置是正确的
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
// SV_Target可以视为COLOR ,虽说他也是作为输出值输出给系统的
// 但它其实是告诉系统把输出的颜色值存储到RenderTarget中
// 所以这里我们用SV_Target
fixed4 frag (v2f i) : SV_Target
{
// 采样2D纹理贴图
fixed4 col = tex2D(_MainTex, i.uv);
// 应用雾
UNITY_APPLY_FOG(i.fogCoord, col);
// 返回经过处理后的最终色彩
return col;
}
ENDCG
}
}
} v2f vert (appdata v)
{
v2f o;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
// 模型空间转到世界空间
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
// 压Y轴位置 这里把世界空间下顶点的y减去最低部y的值乘上一个系数
// 然后再用y去减去这个值,就可以通过这个系数来控制兔子被压扁的程度
float y = worldPos.y - (worldPos.y - _Bottom) * _Value;
// 最终世界空间位置
float3 tempWorld = float3(worldPos.x,y,worldPos.z);
// 世界空间转裁剪空间
o.vertex = UnityWorldToClipPos(tempWorld);
return o;
} Properties
{
_Value ("压扁系数",Range(0, 1)) = 0
_Bottom ("底部", float) = 0
}
float _Value;
float _Bottom;



struct v2f
{
float2 uv : TEXCOORD0;
// 计算光照需要用到法线和世界位置
// 通常使用TEXCOORDn语义来修饰float2, float3, float4类型
float3 worldNormal: TEXCOORD1;
float3 worldPos:TEXCOORD2;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
}; v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
// 模型空间转到世界空间
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
// 法线向量归一化
o.worldNormal = normalize(UnityObjectToWorldNormal(v.normal));
return o;
} fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// 得到光照方向
float3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos);
// NoL代表表面接受的能量大小
float NoL = dot(i.worldNormal, worldLightDir);
// 计算half-lambert亮度值
float halfLambert = NoL * 0.5 + 0.5;
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col * halfLambert;
}

Properties
{
_MainTex("Texture",2D) = "white" {}
_OutlineWidth ("Outline Width", Range(0.01, 1)) = 0.01
_OutLineColor ("OutLine Color", Color) = (0.5,0.5,0.5,1)
} Shader "Unlit/ToonShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_OutlineWidth ("Outline Width", Range(0.01, 1)) = 0.01
_OutLineColor ("OutLine Color", Color) = (0.5,0.5,0.5,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
Pass
{
// 开启前向剔除 表示剔除前面 只显示背面
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// 线条宽度
float _OutlineWidth;
// 线条颜色
float4 _OutLineColor;
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
// 法线
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
// 顶点沿着法线方向外扩(放大模型)
float4 newVertex = float4(v.vertex.xyz + v.normal * _OutlineWidth * 0.01 ,1);
// UnityObjectToClipPos(v.vertex) 将模型空间下的顶点转换到齐次裁剪空间
o.vertex = UnityObjectToClipPos(newVertex);
return o;
}
half4 frag(v2f i) : SV_TARGET
{
// 返回线条色彩
return _OutLineColor;
}
ENDCG
}
}
}
// 得到顶点法线
float3 normal = normalize(i.worldNormal);
// 得到光照方向
float3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos);
// NoL代表表面接受的能量大小
float NoL = dot(i.worldNormal, worldLightDir);
// 计算half-lambert亮度值
float halfLambert = NoL * 0.5 + 0.5;
// 通过亮度值计算线性ramp
float ramp = linearstep(_RampStart, _RampStart + _RampSize, halfLambert);
float step = ramp * _RampStep; // 使每个色阶大小为1, 方便计算
float gridStep = floor(step); // 得到当前所处的色阶
float smoothStep = smoothstep(gridStep, gridStep + _RampSmooth, step) + gridStep;
ramp = smoothStep / _RampStep; // 回到原来的空间
// 得到最终的ramp色彩
float3 rampColor = lerp(_DarkColor, _LightColor, ramp);
rampColor *= col;
// 得到视向量
float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
// 计算half向量, 使用Blinn-phone计算高光
float3 halfDir = normalize(viewDir + worldLightDir);
// 计算NoH用于计算高光
float NoH = dot(normal, halfDir);
// 计算高光亮度值
float blinnPhone = pow(max(0, NoH), _SpecPow * 128.0);
// 计算高光色彩
float3 specularColor = smoothstep(0.7 - _SpecSmooth / 2, 0.7 + _SpecSmooth / 2, blinnPhone)
* _SpecularColor * _SpecIntensity; 
// 计算NoV用于计算边缘光
float NoV = dot(i.worldNormal, viewDir);
// 计算边缘光亮度值
float rim = (1 - max(0, NoV)) * NoL;
// 计算边缘光颜色
float3 rimColor = smoothstep(_RimThreshold - _RimSmooth / 2, _RimThreshold + _RimSmooth / 2, rim) * _RimColor; // 混合颜色
float3 finalColor = saturate(rampColor + specularColor + rimColor);
return float4(finalColor,1);
Shader "Custom/ToonShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_OutlineWidth ("Outline Width", Range(0.01, 1)) = 0.01
_OutLineColor ("OutLine Color", Color) = (0.5,0.5,0.5,1)
_RampStart ("交界起始 RampStart", Range(0.1, 1)) = 0.3
_RampSize ("交界大小 RampSize", Range(0, 1)) = 0.1
[IntRange] _RampStep("交界段数 RampStep", Range(1,10)) = 1
_RampSmooth ("交界柔和度 RampSmooth", Range(0.01, 1)) = 0.1
_DarkColor ("暗面 DarkColor", Color) = (0.4, 0.4, 0.4, 1)
_LightColor ("亮面 LightColor", Color) = (0.8, 0.8, 0.8, 1)
_SpecPow("SpecPow 光泽度", Range(0, 1)) = 0.1
_SpecularColor ("SpecularColor 高光", Color) = (1.0, 1.0, 1.0, 1)
_SpecIntensity("SpecIntensity 高光强度", Range(0, 1)) = 0
_SpecSmooth("SpecSmooth 高光柔和度", Range(0, 0.5)) = 0.1
_RimColor ("RimColor 边缘光", Color) = (1.0, 1.0, 1.0, 1)
_RimThreshold("RimThreshold 边缘光阈值", Range(0, 1)) = 0.45
_RimSmooth("RimSmooth 边缘光柔和度", Range(0, 0.5)) = 0.1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal: NORMAL; // 计算光照需要用到模型法线
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
// 计算光照需要用到法线和世界位置
float3 worldNormal: TEXCOORD1;
float3 worldPos:TEXCOORD2;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _RampStart;
float _RampSize;
float _RampStep;
float _RampSmooth;
float3 _DarkColor;
float3 _LightColor;
float _SpecPow;
float3 _SpecularColor;
float _SpecIntensity;
float _SpecSmooth;
float3 _RimColor;
float _RimThreshold;
float _RimSmooth;
float linearstep (float min, float max, float t)
{
return saturate((t - min) / (max - min));
}
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
// 向下传输这些数据
o.worldNormal = normalize(UnityObjectToWorldNormal(v.normal));
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
//------------------------ 漫反射 ------------------------
// 得到顶点法线
float3 normal = normalize(i.worldNormal);
// 得到光照方向
float3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos);
// NoL代表表面接受的能量大小
float NoL = dot(i.worldNormal, worldLightDir);
// 计算half-lambert亮度值
float halfLambert = NoL * 0.5 + 0.5;
//------------------------ 高光 ------------------------
// 得到视向量
float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
// 计算half向量, 使用Blinn-phone计算高光
float3 halfDir = normalize(viewDir + worldLightDir);
// 计算NoH用于计算高光
float NoH = dot(normal, halfDir);
// 计算高光亮度值
float blinnPhone = pow(max(0, NoH), _SpecPow * 128.0);
// 计算高光色彩
float3 specularColor = smoothstep(0.7 - _SpecSmooth / 2, 0.7 + _SpecSmooth / 2, blinnPhone)
* _SpecularColor * _SpecIntensity;
//------------------------ 边缘光 ------------------------
// 计算NoV用于计算边缘光
float NoV = dot(i.worldNormal, viewDir);
// 计算边缘光亮度值
float rim = (1 - max(0, NoV)) * NoL;
// 计算边缘光颜色
float3 rimColor = smoothstep(_RimThreshold - _RimSmooth / 2, _RimThreshold + _RimSmooth / 2, rim) * _RimColor;
//------------------------ 色阶 ------------------------
// 通过亮度值计算线性ramp
float ramp = linearstep(_RampStart, _RampStart + _RampSize, halfLambert);
float step = ramp * _RampStep; // 使每个色阶大小为1, 方便计算
float gridStep = floor(step); // 得到当前所处的色阶
float smoothStep = smoothstep(gridStep, gridStep + _RampSmooth, step) + gridStep;
ramp = smoothStep / _RampStep; // 回到原来的空间
// 得到最终的ramp色彩
float3 rampColor = lerp(_DarkColor, _LightColor, ramp);
rampColor *= col;
// 混合颜色
float3 finalColor = saturate(rampColor + specularColor + rimColor);
return float4(finalColor,1);
}
ENDCG
}
Pass
{
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
// 法线
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
// 线条宽度
float _OutlineWidth;
// 线条颜色
float4 _OutLineColor;
v2f vert (appdata v)
{
v2f o;
float4 newVertex = float4(v.vertex.xyz + normalize(v.normal) * _OutlineWidth * 0.05,1);
o.vertex = UnityObjectToClipPos(newVertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return _OutLineColor;
}
ENDCG
}
}
fallback"Diffuse"
}







// 计算ray march信息
// 每个像素发射射线
float3 rayOriginal = 0;
float3 totalDir = i.worldPos - rayOriginal;
float3 rayDir = normalize(totalDir);
// 拓展球面来计算march的起始点
// rcp -> reciprocal 求倒数
// 天空曲率
float skyCurvatureFactor = rcp(rayDir.y + _SkyCurvature);
// 无数条射线像外发射 就会形成一个球面 *天空曲率 就可以把它拍成一个球
float3 basicRayPlane = rayDir * skyCurvatureFactor * _AurorasTiling ;
// 从哪开始步进(大气层最底面)
float3 rayMarchBegin = rayOriginal + basicRayPlane;
// 一步的大小
float stepSize = rcp(_RayMarchStep);
for (float i = 0; i < _RayMarchStep; i += 1)
{
float curStep = stepSize * i;
// 初始的几次采样贡献更大, 我们用二次函数着重初始采样
curStep = curStep * curStep;
// 当前步进距离
float curDistance = curStep * _RayMarchDistance;
// 步进后的位置
float3 curPos = rayMarchBegin + rayDir * curDistance * skyCurvatureFactor;
float2 uv = float2(-curPos.x,curPos.z);
float curAuroras = tex2D(_MainTex, TRANSFORM_TEX(uv, _MainTex)).r;
}
// 极光色彩累积计算
// 由于sin的范围是-1到1,所以要先把颜色范围转换到-1到1之间,这通过i计算出当前步进层的色彩
// 最后 * 0.5再加0.5就返回到了原本的0-1的范围区间
float3 curColor = sin((_AurorasColor * 2 - 1) + i * 0.043) * 0.5 + 0.5;
// 取两步色彩的平均值 使颜色更接近于本色
avgColor = (avgColor + curColor) / 2;
// 混合颜色
color += avgColor * curAuroras * stepSize; // for 内
// 强度衰减
curAuroras = curAuroras * saturate(1 - pow(curDistance, 1 - _AurorasAttenuation));
// for 外
// 强度
color *= _AurorasIntensity; // for 内
// 计算扰动uv
float2 warp_vec = tex2D(_AurorasNoiseTex,TRANSFORM_TEX((uv * 2 + _Time.y * _AurorasSpeed),_AurorasNoiseTex));
// 采样当前的噪声强度
float curNoise = tex2D(_MainTex, TRANSFORM_TEX((uv + warp_vec * 0.1), _MainTex)).r;
// 混合颜色
color += avgColor * curNoise * stepSize;
const float starTime = _Time.y * _StarShinningSpeed;
// 计算叠加区间的两层星星UV
const float2 beginMove = floor(starTime) * 0.3;
const float2 endMove = ceil(starTime) * 0.3;
const float2 beginUV = i.uv + beginMove;
const float2 endUV = i.uv + endMove;
// 采样两层星星的值
float beginNoise = tex2D(_StarNoiseTex, TRANSFORM_TEX(beginUV,_StarNoiseTex)).r;
float endNoise = tex2D(_StarNoiseTex, TRANSFORM_TEX(endUV,_StarNoiseTex)).r; // 减少星星
beginNoise = saturate(beginNoise - (1 - _StarCount)) / _StarCount;
endNoise = saturate(endNoise - (1 - _StarCount)) / _StarCount;
const float fracStarTime = frac(starTime);
// 混合两层星星值
starColor = saturate(beginNoise - fracStarTime) + saturate(endNoise - (1 - fracStarTime)); // 混合天际线
color *= saturate(rayDir.y / _SkyLineSize + _SkyLineBasePow); Shader "Custom/Auroras"
{
Properties
{
_MainTex ("AurorasTexture", 2D) = "white" {}
_AurorasNoiseTex ("AurorasNoise", 2D) = "white" {}
_StarNoiseTex ("StarNoise", 2D) = "white" {}
_SkyColor ("天空颜色 SkyColor", Color) = (0.4, 0.4, 0.4, 1)
_AurorasColor ("极光颜色 AurorasColor", Color) = (0.4, 0.4, 0.4, 1)
_AurorasTiling("极光平铺 AurorasTiling", Range(0.1, 10)) = 0.4
_AurorasSpeed ("极光变化速度 AurorasSpeed", Range(0.01, 1)) = 0.1
_AurorasIntensity("极光强度 AurorasIntensity", Range(0.1, 20)) = 3
_AurorasAttenuation("极光衰减 AurorasAttenuation", Range(0, 0.99)) = 0.4
_SkyCurvature ("天空曲率 SkyCurvature", Range(0, 10)) = 0.4
_RayMarchDistance("步进距离 RayMarchDistance", Range(0.01, 1)) = 2.5
[IntRange] _RayMarchStep("步进步数 RayMarchStep", Range(1,128)) = 64
_SkyLineSize("天际线大小 SkyLineSize", Range(0, 1)) = 0.06
_SkyLineBasePow("天际线基础强度 SkyLineBasePow", Range(0, 1)) = 0.1
_StarShinningSpeed ("星星闪烁速度 StarShinningSpeed", Range(0, 1)) = 0.1
_StarCount("星星数量 StarCount", Range(0,1)) = 0.3
}
SubShader
{
Tags
{
"RenderType"="Opaque"
}
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(v.vertex, unity_ObjectToWorld);
o.uv = v.uv;
return o;
}
float3 _AurorasColor;
float3 _SkyColor;
float _AurorasIntensity;
float _AurorasTiling;
float _AurorasSpeed;
float _AurorasAttenuation;
float _SkyCurvature;
float _RayMarchDistance;
float _RayMarchStep;
float _SkyLineSize;
float _SkyLineBasePow;
float _StarShinningSpeed;
float _StarCount;
sampler2D _MainTex;
sampler2D _AurorasNoiseTex;
sampler2D _StarNoiseTex;
float4 _StarNoiseTex_ST;
float4 _AurorasNoiseTex_ST;
float4 _MainTex_ST;
fixed4 frag (v2f i) : SV_Target
{
tex2D(_StarNoiseTex, TRANSFORM_TEX(i.uv,_StarNoiseTex)).r;
// 星星
float starColor = 0;
const float starTime = _Time.y * _StarShinningSpeed;
// 计算叠加区间的两层星星UV
const float2 beginMove = floor(starTime) * 0.3;
const float2 endMove = ceil(starTime) * 0.3;
const float2 beginUV = i.uv + beginMove;
const float2 endUV = i.uv + endMove;
// 采样两层星星的值
float beginNoise = tex2D(_StarNoiseTex, TRANSFORM_TEX(beginUV,_StarNoiseTex)).r;
float endNoise = tex2D(_StarNoiseTex, TRANSFORM_TEX(endUV,_StarNoiseTex)).r;
// 减少星星
beginNoise = saturate(beginNoise - (1 - _StarCount)) / _StarCount;
endNoise = saturate(endNoise - (1 - _StarCount)) / _StarCount;
const float fracStarTime = frac(starTime);
// 混合两层星星值
starColor = saturate(beginNoise - fracStarTime) + saturate(endNoise - (1 - fracStarTime));
// 计算ray march信息
// 每个像素发射射线
float3 rayOriginal = 0;
float3 totalDir = i.worldPos - rayOriginal;
float3 rayDir = normalize(totalDir);
//clip(rayDir.y);
// 拓展球面来计算march的起始点
// reciprocal 求倒数
// 天空曲率
float skyCurvatureFactor = rcp(rayDir.y + _SkyCurvature);
// 本质为模拟地球大气
// 无数条射线像外发射 就会形成一个球面 *天空曲率 就可以把它拍成一个球
float3 basicRayPlane = rayDir * skyCurvatureFactor * _AurorasTiling ;
// 从哪开始步进
float3 rayMarchBegin = rayOriginal + basicRayPlane;
// ray march
float3 color = 0;
float3 avgColor = 0;
// 一步的大小
float stepSize = rcp(_RayMarchStep);
for (float i = 0; i < _RayMarchStep; i += 1)
{
float curStep = stepSize * i;
// 初始的几次采样贡献更大, 我们用二次函数着重初始采样
curStep = curStep * curStep;
// 当前步进距离
float curDistance = curStep * _RayMarchDistance;
// 步进后的位置
float3 curPos = rayMarchBegin + rayDir * curDistance * skyCurvatureFactor;
float2 uv = float2(-curPos.x,curPos.z);
// ===== 极光动起来
// 计算扰动uv
float2 warp_vec =
tex2D(_AurorasNoiseTex,TRANSFORM_TEX((uv * 2 + _Time.y * _AurorasSpeed),_AurorasNoiseTex));
// 采样当前的噪声强度
float curNoise = tex2D(_MainTex, TRANSFORM_TEX((uv + warp_vec * 0.1), _MainTex)).r;
//curNoise = tex2D(_MainTex, TRANSFORM_TEX(uv, _MainTex)).r;
// =======================
// 最后加强度衰减
curNoise = curNoise * saturate(1 - pow(curDistance, 1 - _AurorasAttenuation));
// 极光色彩累积计算
// 由于sin的范围是-1到1,所以要先把颜色范围转换到-1到1之间,这通过i计算出当前步进层的色彩
// 最后 * 0.5再加0.5就返回到了原本的0-1的范围区间
float3 curColor = sin((_AurorasColor * 2 - 1) + i * 0.043) * 0.5 + 0.5;
// 取两步色彩的平均值 使颜色更接近于本色
avgColor = (avgColor + curColor) / 2;
// 混合颜色
color += avgColor * curNoise * stepSize;
}
// 强度
color *= _AurorasIntensity;
// 混合天际线
color *= saturate(rayDir.y / _SkyLineSize + _SkyLineBasePow);
// 天空色
color += _SkyColor;
// 星星
color = color + starColor * 0.9;
return fixed4(color, 1);
}
ENDCG
}
}
}


































Pass
{
Tags
{
"LightMode" = "UniversalForward"
}
} Pass
{
Tags
{
"LightMode" = "SRPDefaultUnlit"
}
}


_ShallowWater ("shallowColor", Color) = (1.0, 1.0, 1.0, 1.0)
_DeepWater ("DeepColor", Color) = (1.0, 1.0, 1.0, 1.0) #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"



VertexPositionInputs positionInputs = GetVertexPositionInputs(v.positionOS);
v2f vert(a2v v)
{
v2f o;
VertexPositionInputs positionInputs = GetVertexPositionInputs(v.positionOS);
o.positionCS = positionInputs.positionCS;
o.screenPosition = ComputeScreenPos(positionInputs.positionCS);
return o;
} half4 frag(v2f i) : SV_Target
{
// 通过深度纹理的采样 计算屏幕深度
float sceneRawDepth = SampleSceneDepth(i.screenPosition.xy / i.screenPosition.w);
// 深度纹理的采样结果转换到视图空间下的深度值
float sceneEyeDepth = LinearEyeDepth(sceneRawDepth, _ZBufferParams);
return col;
} // 最终得到水的深度
float waterDepth = sceneEyeDepth - i.screenPosition.w;
// 拿到水的颜色
float3 waterColor = lerp(_ShallowWater, _DeepWater, waterDepth); float surfaceNoiseSample = tex2D(_SurfaceNoise, i.noiseUV + _Time.y * _MoveSpeed * 0.1).r; // 浮沫
float foam = saturate(waterDepth / _FoamDistance);
float surfaceNoise = smoothstep(0, foam, surfaceNoiseSample) ;
// 混合水面透明度
float4 col = float4(waterColor + surfaceNoise * _FoamColor, _WaterAlpha) ;
return col; Shader "URP/Water"
{
Properties
{
_ShallowWater ("shallowColor", Color) = (1.0, 1.0, 1.0, 1.0)
_DeepWater ("DeepColor", Color) = (1.0, 1.0, 1.0, 1.0)
_WaterAlpha("WaterAlpha",Range(0,1)) = 0.5
_SurfaceNoise("Surface Noise", 2D) = "white" {}
_MoveSpeed("MoveSpeed",Range(0,1)) = 0.5
_FoamDistance("Foam Distance",Range(0,10)) = 0.4
_FoamColor("FoamColor", Color) = (1.0, 1.0, 1.0, 1.0)
}
SubShader
{
Tags
{
"RenderPipeline"="UniversalPipeline"
"RenderType"="Transparent"
"Queue"="Transparent"
}
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
float4 _ShallowWater;
float4 _DeepWater;
float _WaterAlpha;
sampler2D _SurfaceNoise;
float4 _SurfaceNoise_ST;
float _MoveSpeed;
float _FoamDistance;
float4 _FoamColor;
// 顶点着色器的输入
struct a2v
{
float3 positionOS : POSITION;
float4 uv : TEXCOORD0;
};
// 顶点着色器的输出
struct v2f
{
float4 positionCS : SV_POSITION;
float4 screenPosition : TEXCOORD0;
float2 noiseUV : TEXCOORD1;
float2 distortUV : TEXCOORD2;
};
v2f vert(a2v v)
{
v2f o;
VertexPositionInputs positionInputs = GetVertexPositionInputs(v.positionOS);
o.positionCS = positionInputs.positionCS;
o.noiseUV = TRANSFORM_TEX(v.uv, _SurfaceNoise);
o.screenPosition = ComputeScreenPos(positionInputs.positionCS);
return o;
}
half4 frag(v2f i) : SV_Target
{
// 通过深度纹理的采样 计算屏幕深度
float sceneRawDepth = SampleSceneDepth(i.screenPosition.xy / i.screenPosition.w);
// 深度纹理的采样结果转换到视图空间下的深度值
float sceneEyeDepth = LinearEyeDepth(sceneRawDepth, _ZBufferParams);
// 因为关心的是这个深度值相对于我们的水面有多深,所以需要把视图深度,减去模型顶点的深度
// 最终得到水的深度
float waterDepth = sceneEyeDepth - i.screenPosition.w;
// 拿到水的颜色
float3 waterColor = lerp(_ShallowWater, _DeepWater, waterDepth);
float surfaceNoiseSample = tex2D(_SurfaceNoise, i.noiseUV + _Time.y * _MoveSpeed * 0.1).r;
// 浮沫
float foam = saturate(waterDepth / _FoamDistance);
float surfaceNoise = smoothstep(0, foam, surfaceNoiseSample) ;
// 混合水面透明度
float4 col = float4(waterColor + surfaceNoise * _FoamColor, _WaterAlpha) ;
return col;
}
ENDHLSL
}
}
}